package tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.util.UUID;

import lib.logging.ILogger;
import lib.messaging.NetworkAddress;
import lib.rpc.ClientRPCMethods;
import lib.rpc.IClientRPCFunctionality;
import lib.rpc.IRPCMethods;
import lib.rpc.RPCInvokePacket;
import lib.rpc.RPCResultCode;
import lib.rpc.RPCResultPacket;

import org.junit.Test;



/**
 * Unittests for {@link ClientRPCMethods}
 *
 * @author abhip
 *
 */
public class ClientRPCMethodsTests {
    private static final NetworkAddress DefaultClientAddress = new NetworkAddress("localhost", 2000);
    private static final String DefaultData = "Test data!";
    private static final String DefaultFileName = "test.txt";
    private static final int DefaultRoundID = 1;
    private static final NetworkAddress DefaultServerAddress =new NetworkAddress("localhost", 1000);

    @Test(expected = IOException.class)
    public void AppendLineToFile_WhenRPCFailedWithIOException_ThrowsIOException()
            throws ClassNotFoundException, IOException {
        // Arrange:
        ILogger logger = new TestLogger();
        IRPCMethods rpcMethodsClientSide = new ClientRPCMethods(logger,
                new IClientRPCFunctionality() {
                    @Override
                    public RPCResultPacket Invoke(
                            RPCInvokePacket rpcInvokePacket)
                            throws IOException, ClassNotFoundException {
                        return new RPCResultPacket(
                                UUID.randomUUID(),
                                DefaultServerAddress,
                                RPCResultCode.FAILED,
                                new IOException("This is a test IOException"));
                    }
                });

        // Act/Assert: It should throw IOException
        rpcMethodsClientSide.AppendLineToFile(
                DefaultClientAddress,
                DefaultServerAddress,
                DefaultRoundID,
                DefaultFileName,
                DefaultData);
    }

    @Test(expected = ClassNotFoundException.class)
    public void CreateFile_WhenRPCFailedWithClassNotFoundException_ThrowsClassNotFoundException()
            throws ClassNotFoundException, IOException {
        // Arrange:
        ILogger logger = new TestLogger();
        IRPCMethods rpcMethodsClientSide = new ClientRPCMethods(logger,
                new IClientRPCFunctionality() {
                    @Override
                    public RPCResultPacket Invoke(
                            RPCInvokePacket rpcInvokePacket)
                            throws IOException, ClassNotFoundException {
                        return new RPCResultPacket(
                                UUID.randomUUID(),
                                DefaultServerAddress,
                                RPCResultCode.FAILED,
                                new ClassNotFoundException(
                                        "This is a test classNotFoundException"));
                    }
                });

        // Act/Assert: It should throw ClassNotFoundException
        rpcMethodsClientSide.CreateFile(
                DefaultClientAddress,
                DefaultServerAddress,
                DefaultRoundID,
                DefaultFileName);
    }

    @Test
    public void CreateFile_WhenRPCFailedWithSomeError_LogsError()
            throws ClassNotFoundException, IOException {
        // Arrange:
        final String testErrorMessage = "Some error occurred!";
        TestLogger logger = new TestLogger();
        IRPCMethods rpcMethodsClientSide = new ClientRPCMethods(logger,
                new IClientRPCFunctionality() {
                    @Override
                    public RPCResultPacket Invoke(
                            RPCInvokePacket rpcInvokePacket)
                            throws IOException, ClassNotFoundException {
                        return new RPCResultPacket(
                                UUID.randomUUID(),
                                DefaultServerAddress,
                                RPCResultCode.FAILED,
                                testErrorMessage);
                    }
                });

        // Act:
        rpcMethodsClientSide.CreateFile(
                DefaultClientAddress,
                DefaultServerAddress,
                DefaultRoundID,
                DefaultFileName);

        // Assert:
        assertEquals("Logger.LogError should have been called once", 1,
                logger.ErrorCallsCount());
        String loggedErrorMsg = logger.ErrorCalls().get(0);
        assertTrue("loggedErrorMsg should contain testErrorMessage",
                loggedErrorMsg.contains(testErrorMessage));
    }

    @Test
    public void CreateFile_WhenRPCFailedWithUnexpectedException_LogsExceptionAsError()
            throws ClassNotFoundException, IOException {
        // Arrange:
        TestLogger logger = new TestLogger();
        IRPCMethods rpcMethodsClientSide = new ClientRPCMethods(logger,
                new IClientRPCFunctionality() {
                    @Override
                    public RPCResultPacket Invoke(
                            RPCInvokePacket rpcInvokePacket)
                            throws IOException, ClassNotFoundException {
                        return new RPCResultPacket(
                                UUID.randomUUID(),
                                DefaultServerAddress,
                                RPCResultCode.FAILED,
                                new UnsupportedOperationException("This is an unexpected exception!"));
                    }
                });

        // Act:
        rpcMethodsClientSide.CreateFile(
                DefaultClientAddress,
                DefaultServerAddress,
                DefaultRoundID,
                DefaultFileName);

        // Assert:
        assertEquals("Logger.LogError should have been called once", 1,
                logger.ErrorCallsCount());
        String loggedErrorMsg = logger.ErrorCalls().get(0);
        assertTrue("loggedErrorMsg should contain testErrorMessage",
                loggedErrorMsg.contains("This is an unexpected exception!"));
    }

    @Test
    public void CreateFile_WhenRPCSuceeds_LogsInfo()
            throws ClassNotFoundException, IOException {
        // Arrange:
        TestLogger logger = new TestLogger();
        IRPCMethods rpcMethodsClientSide = new ClientRPCMethods(logger,
                new IClientRPCFunctionality() {
                    @Override
                    public RPCResultPacket Invoke(
                            RPCInvokePacket rpcInvokePacket)
                            throws IOException, ClassNotFoundException {
                        return new RPCResultPacket(
                                UUID.randomUUID(),
                                DefaultServerAddress,
                                RPCResultCode.SUCCESS,
                                null);
                    }
                });

        // Act:
        rpcMethodsClientSide.CreateFile(
                DefaultClientAddress,
                DefaultServerAddress,
                DefaultRoundID,
                DefaultFileName);

        // Assert:
        assertEquals("Logger.LogInfo should have been called once", 1,
                logger.InfoCallsCount());
        String loggedInfoMsg = logger.InfoCalls().get(0);
        assertTrue("loggedInfoMsg should contain success",
                loggedInfoMsg.startsWith("RPC for CreateFile was successful"));
    }


}
